<?php
/************************
 *  Codenova.pl Project
 *  Created by: Kamil Mrzygłód
 *
 *  (c) 2013 by Codenova.pl
 *  All Right Reserved
 ***********************/

namespace Gotcha\App\Extensions;

// Gotcha routing class

use Gotcha\App\Types\Arrays;
use Gotcha\App\Types\String;

class RoutingExtension extends FileSystemExtension {
    // Routes
    private $routes = array();

    // Current request
    public $routingCurrentRequest;

    public function __construct() {
        if (isset($_SERVER['PATH_INFO'])) {
            $this->routingCurrentRequest = $_SERVER['PATH_INFO'];
        } elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
            $this->routingCurrentRequest = $_SERVER['ORIG_PATH_INFO'];
        } else {
            $this->routingCurrentRequest = '/';
        }
    }

    public function __toString() {
        return (string)$this->routingCurrentRequest;
    }

    public function processRequest(String $file) {
        parent::processRoutesFile($file);

        // Retrieve routes
        $this->routes = parent::returnRoutesValues();

        // Check if route exists
        if (array_key_exists($this->routingCurrentRequest, $this->routes)) {
            if (parent::CheckDirExistence(new String('Modules/'.$this->routes[$this->routingCurrentRequest]['module'])) === true) {
                // Module exists, search for given controller
                if (parent::CheckFileExistence(new String($this->routes[$this->routingCurrentRequest]['controller'].'Controller.php'), new String('Modules/'.$this->routes[$this->routingCurrentRequest]['module'].'/Controllers/')) === true) {
                    // Controller exists, we can load a file
                    $controllerFile = new String('Modules/'.$this->routes[$this->routingCurrentRequest]['module'].'/Controllers/'.$this->routes[$this->routingCurrentRequest]['controller'].'Controller.php');
                    /** @noinspection PhpIncludeInspection */
                    require_once $controllerFile;

                    // To enable pre/post-processing we have to check
                    // whether class implements this methods
                    try {
                        $reflectionClass = new \ReflectionClass('Modules\\'.$this->routes[$this->routingCurrentRequest]['module'].'\Controllers\\'.$this->routes[$this->routingCurrentRequest]['controller'].'Controller');

                        // Check for pre-processing method existence
                        $preRenderFunctionName = new String('preRender');
                        foreach($reflectionClass->getMethods() as $method) {
                            if($method->name === $preRenderFunctionName->string) {
                                $preAction = $reflectionClass->getMethod($preRenderFunctionName->string);
                                $preAction->invoke(new $preAction->class(), null);
                                break;
                            }
                        }

                        // We hardcoded place from each module starts.
                        // It's more an MVC idea but to keep code and
                        // module logic clean, we are forcing storing
                        // all controllers in Modules/[MODULE_NAME]/Controllers
                        // dir.
                        $reflectionMethodName = new String($this->routes[$this->routingCurrentRequest]['action']);
                        $reflectionMethod = $reflectionClass->getMethod($reflectionMethodName->string);

                        // Invoke method linked with controller
                        $reflectionMethod->invoke(new $reflectionMethod->class(), null);

                        // Check for pre-processing method existence
                        $postRenderFunctionName = new String('postRender');
                        foreach($reflectionClass->getMethods() as $method) {
                            if($method->name === $postRenderFunctionName->string) {
                                $postAction = $reflectionClass->getMethod($postRenderFunctionName->string);
                                $postAction->invoke(new $postAction->class(), null);
                                break;
                            }
                        }

                        // Return to prevent further executing of the code
                        return;
                    } catch (\Exception $e) {
                        ErrorsExtension::throwErrorAndSaveLog(new String('CLASS_OR_METHOD_INITIALIZATION_ERROR'), new Arrays(array()));
                        exit;
                    }
                } else {
                    // Controller does not exist
                    self::header404();
                }
            } else {
                // Module does not exist
                self::header404();
            }
        }

        self::header404();
    }

    private function header404() {
        // Set 404 header
        header('HTTP/1.0 404 Not Found');
        require_once GOTCHA_ROOT_DIR.'/App/Templates/Errors/404.phtml';
        exit;
    }
}